﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(HairUnitGroupShape))]
public class HairUnitGroupShapeEditor : Editor
{
    public override void OnInspectorGUI()
    {
        var o = (HairUnitGroupShape)target;
        base.OnInspectorGUI();
        if (o.smr == null) o.Start();
        if (GUILayout.Button("Revert"))
        {
            o.Revert();
        }
        if (GUILayout.Button("Apply&Solve"))
        {
            o.Revert();
            o.Apply();
            o.SolveAll();
        }
        if (GUILayout.Button("Apply"))
        {
            o.Revert();
            o.Apply();
        }
        if (GUILayout.Button("SolveAll"))
        {
            o.SolveAll();
        }
        if (GUILayout.Button("SolveFirst"))
        {
            o.Apply();
            o.SolveNext();
        }
        if (GUILayout.Button("SolveNext"))
        {
            o.SolveNext();
        }
    }
    private void OnSceneGUI()
    {
        var o = (HairUnitGroupShape)target;
        o.DrawLine();
    }
}
#endif

public class HairUnitGroupShape : MonoBehaviour
{
    public AnimationCurve curveShapeY;
    public AnimationCurve curveShapeZ;
    public List<Transform> bones;
    public SkinnedMeshRenderer smr;
    public Transform emitter;
    public Transform end;
    //public List<Vector3> boneAxises;
    public List<Vector3> targetPoses;
    public List<Vector2> curvePoses;
    public Vector2 endRange;
    public Vector3 originScale;
    public Material material;
    public int segment = 200;
    public float duration;
    public float factor;
    public List<Vector3> curveWorldPoses;
    public void Start()
    {
        smr = GetComponentInChildren<SkinnedMeshRenderer>();
        bones = new List<Transform>(smr.bones);
        bones.RemoveAt(0);
    }
    void CacheCurve()
    {
        min = Vector2.zero;
        curvePoses = new List<Vector2>();
        factor = duration / segment;
        float t;
        Vector2 curvePos;
        curveWorldPoses = new List<Vector3>();
        curveWorldPoses.Add(emitter.TransformPoint(Vector2.zero));
        for (int i = 0; i < segment; i++)
        {
            t = i * factor;
            curvePos = new Vector2(curveShapeY.Evaluate(t), curveShapeZ.Evaluate(t));
            curvePos = Vector2.Scale(curvePos, endRange);
            curvePoses.Add(curvePos);
            curveWorldPoses.Add(emitter.TransformPoint(curvePoses[i].toYZ()));
        }
    }
    Vector3 min;
    Vector3 CalCurvePoint(Vector3 bonePose, float boneLength, ref int preSegmentIndex)
    {
        float dis;
        float osMin = 999f;
        float os;
        for (int i = preSegmentIndex + 1; i < segment; i++)
        {
            dis = Vector3.Distance(curveWorldPoses[i], bonePose);
            os = Mathf.Abs(dis - boneLength);
            if (os < osMin)
            {
                preSegmentIndex = i;
                min = curveWorldPoses[i];
                osMin = os;
            }
        }
        return min;
    }
    internal void DrawLine()
    {
        CacheCurve();
        for (int i = 1; i < segment; i++)
        {
            Debug.DrawLine(curveWorldPoses[i - 1], curveWorldPoses[i], Color.green);
        }
        if (targetPoses == null || targetPoses.Count <= 0) return;
        for (int i = 1; i < targetPoses.Count; i++)
        {
            Debug.DrawLine(targetPoses[i - 1], targetPoses[i], Color.red);
        }
    }
    public void Apply()
    {
        Transform bone;
        endRange = Tool.Abs(emitter.InverseTransformPoint(end.position)).yzV2();
        targetPoses = new List<Vector3>();
        for (int i = 0; i < bones.Count; i++)
        {
            bone = bones[i];
        }
        CacheCurve();
        segmentPointer = -1;
        solveIndex = -1;
    }
    public int segmentPointer;
    public int solveIndex = 0;
    public void SolveNext()
    {
        solveIndex++;
        if (solveIndex >= bones.Count) solveIndex = 0;
        SolveBone(solveIndex);
    }
    public void SolveAll()
    {
        for (int i = 0; i < bones.Count; i++)
        {
            SolveBone(i);
        }
    }
    private void SolveBone(int i)
    {
        Transform bone = bones[i];

        var boneLocalPos = emitter.InverseTransformPoint(bone.position).yzV2();
        var parentLocalPos = emitter.InverseTransformPoint(bone.parent.position).yzV2();

        var dist = Vector3.Distance(bone.position, bone.parent.position);
        targetPoses.Add(CalCurvePoint(bone.parent.position, dist, ref segmentPointer));

        var p = bone.parent.position;
        var to = targetPoses[i] - p;
        var from = bone.position - p;

        this.parent.position = p;
        this.to.position = targetPoses[i];
        this.from.position = bone.position;

        var rot = Quaternion.FromToRotation(from, to);
        bone.parent.rotation = rot * bone.parent.rotation;
    }
    public Transform parent;
    public Transform to;
    public Transform from;
    public Vector3 unitScale;
    public HairUnitBaker baker;
    internal void Revert()
    {
        var o = GetComponentInChildren<Animator>().gameObject;
        o.transform.localScale = originScale;
        //if (Application.isPlaying) return;
        //PrefabUtility.RevertPrefabInstance(o);
        //smr.sharedMaterials = new Material[] { material, material };
        //o.transform.localScale = originScale;
        
        //baker = GetComponentInChildren<HairUnitBaker>();
        //baker.scale = unitScale;
        //baker.Init();
        //baker.ScaleMesh();
        //baker.ScaleBone();
    }
}
